#include "./graphics.h"
#include "skia/SkCanvas.h"
#include "skia/SkDrawFilter.h"
#include "skia/SkTypeface.h"
#include "skia/SkTextBox.h"

#define PI 3.1415926535897932384626f

using namespace std;
using namespace MiniUI;
using namespace MiniUI::Type;

Graphics::Graphics()
: m_pCanvas(nullptr)
, m_pPaint(nullptr)
, m_pPath(nullptr)
, m_bPushPaint(false)
{
    m_pPaintStack = new stack<SkPaint>;
    m_pPaint = new SkPaint();
    m_pPaint->setARGB(0xFF, 0, 0, 0);
    m_pPaint->setAntiAlias(true);
    m_pPaint->setFilterBitmap(true);
    m_pPaint->setTextEncoding(SkPaint::kUTF8_TextEncoding);
    m_pPaint->setSubpixelText(true);
    m_pPaint->setTextSize(SkIntToScalar(12));
    m_pPaint->setStrokeCap(SkPaint::kRound_Cap);
    m_pPaint->setStrokeJoin(SkPaint::kRound_Join);
    m_pPath = new SkPath();
}

Graphics::~Graphics()
{
    if (m_pCanvas)
    {
        delete m_pCanvas;
        m_pCanvas = nullptr;
    }
    if (m_pPaint)
    {
        delete m_pPaint;
        m_pPaint = nullptr;
    }
    if (m_pPaintStack)
    {
        delete m_pPaintStack;
        m_pPaintStack = nullptr;
    }
    delete m_pPath;
    m_pPath = nullptr;
}

void Graphics::Rebuild(Bitmap* bitmap)
{
    if (!m_pCanvas)
    {
        delete m_pCanvas;
        m_pCanvas = new SkCanvas();
    }

    m_pCanvas->setBitmapDevice(bitmap->m_bitmap);
    m_pCanvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
}

void Graphics::Clear()
{
    if (!m_pCanvas) return;
    m_pCanvas->clear(0);
}

void Graphics::Save()
{
    m_pCanvas->save();
    m_pPaintStack->push(*m_pPaint);
    m_bPushPaint = true;
}

void Graphics::SaveMatrix()
{
    m_pCanvas->save(SkCanvas::kMatrix_SaveFlag);
    m_bPushPaint = false;
}

void Graphics::Restore()
{
    m_pCanvas->restore();
    if (!m_bPushPaint)
        return;

    if (!m_pPaintStack->empty())
    {
        *m_pPaint = m_pPaintStack->top();
        m_pPaintStack->pop();
    }
    else
    {
        m_pPaint->reset();
        m_pPaint->setAntiAlias(true);
        m_pPaint->setFilterBitmap(true);
        m_pPaint->setStrokeCap(SkPaint::kRound_Cap);
        m_pPaint->setStrokeJoin(SkPaint::kRound_Join);
    }
}

void Graphics::Translate(int x, int y)
{
    if (!m_pCanvas) return;
    m_pCanvas->translate(SkIntToScalar(x), SkIntToScalar(y));
}

void Graphics::SetRectClip(const Type::MiniRect& rcClip, bool bIntersect)
{
    if (!m_pCanvas) return;
    SkRect rect;
    rect.set(SkIntToScalar(rcClip.left), SkIntToScalar(rcClip.top), 
        SkIntToScalar(rcClip.right), SkIntToScalar(rcClip.bottom));
    bool r = m_pCanvas->clipRect(rect, bIntersect ? SkRegion::kIntersect_Op : SkRegion::kReplace_Op);
}

void Graphics::SetCircleClip(const Type::MiniRect& rcClip, bool bIntersect)
{
    if (!m_pCanvas) return;
    m_pPath->reset();

    SkScalar midX = SkIntToScalar(rcClip.left + rcClip.Width() / 2);
    SkScalar midY = SkIntToScalar(rcClip.top + rcClip.Height() / 2);
    SkScalar left = SkIntToScalar(rcClip.left);
    SkScalar top = SkIntToScalar(rcClip.top);
    SkScalar right = SkIntToScalar(rcClip.right);
    SkScalar bottom = SkIntToScalar(rcClip.bottom);

    m_pPath->moveTo(midX, top);
    m_pPath->cubicTo(midX, top, 
                     right, top, 
                     right, midY);
    m_pPath->cubicTo(right, midY, 
                     right, bottom, 
                     midX, bottom);
    m_pPath->cubicTo(midX, bottom, 
                     left, bottom, 
                     left, midY);
    m_pPath->cubicTo(left, midY, 
                     left, left, 
                     midX, top);
    m_pPath->close();

    m_pCanvas->clipPath(*m_pPath, bIntersect ? SkRegion::kIntersect_Op : SkRegion::kReplace_Op);
}

void Graphics::SetDrawColor(const MiniARGB& color)
{
    if (!m_pCanvas) return;
    m_pPaint->setARGB(color.alpha, color.red, color.green, color.blue);
}

void Graphics::SetAlpha(byte alpha)
{
    if (!m_pCanvas) return;
    m_pPaint->setAlpha(alpha);
}

void Graphics::SetStrokeWidth(int width)
{
	if (!m_pCanvas) return;
	m_pPaint->setStrokeWidth(SkIntToScalar(width));
}

void Graphics::SetTextSize(int size)
{
    if (!m_pCanvas) return;
    m_pPaint->setTextSize(SkIntToScalar(size));
}

void Graphics::SetFontStyle(uint style)
{
    SkTypeface::Style skStyle;
    if ((style & FONTSTYLE_BOLD) && (style & FONTSTYLE_ITALIC))
    {
        skStyle = SkTypeface::kBoldItalic;
    }
    else if (style & FONTSTYLE_BOLD)
    {
        skStyle = SkTypeface::kBold;
    }
    else if (style & FONTSTYLE_ITALIC)
    {
        skStyle = SkTypeface::kItalic;
    }
    else
    {
        skStyle = SkTypeface::kNormal;
    }

    if (style & FONTSTYLE_UNDERLINE)
    {
        m_pPaint->setUnderlineText(true);
    }
    else
    {
        m_pPaint->setUnderlineText(false);
    }

    if (style & FONTSTYLE_STRIKE)
    {
        m_pPaint->setStrikeThruText(true);
    }
    else
    {
        m_pPaint->setStrikeThruText(false);
    }
    SkTypeface* typeface = m_pPaint->getTypeface();
    m_pPaint->setTypeface(SkTypeface::CreateFromTypeface(typeface, skStyle))->unref();

}

void Graphics::SetFontFaceName(const char* szFaceName)
{
    if (szFaceName)
    {
        SkTypeface* typeface = m_pPaint->getTypeface();
        SkTypeface::Style style;
        if (typeface)
        {
            style = typeface->style();
        }
        else
        {
            style = SkTypeface::kNormal;
        }
        m_pPaint->setTypeface(SkTypeface::CreateFromName(szFaceName, style))->unref();
    }
}

void Graphics::DrawText(const string& text, const MiniRect& rect, uint align, bool bMultiline)
{
    if (!m_pCanvas) return;
    if (text.empty()) return;

    if (align & TEXTALIGN_CENTER)
    {
        m_pPaint->setTextAlign(SkPaint::kCenter_Align);
    }
    else if (align & TEXTALIGN_RIGHT)
    {
        m_pPaint->setTextAlign(SkPaint::kRight_Align);
    }
    else
    {
        m_pPaint->setTextAlign(SkPaint::kLeft_Align);
    }

    SkRect rcTextBox;
    rcTextBox.iset(rect.left, rect.top, rect.right, rect.bottom);

    if (bMultiline)
    {
        SkTextBox textBox;
        textBox.setBox(rcTextBox);
        textBox.draw(m_pCanvas, text.c_str(), text.length(), *m_pPaint);
    }
    else
    {
        SkRect rcText;
        rcText.setEmpty();
        m_pPaint->measureText(text.c_str(), text.length(), &rcText);

        SkScalar x = 0;
        SkScalar y = 0;
        if (align & TEXTALIGN_CENTER)
        {
            x = rcTextBox.fLeft + rcTextBox.width() / 2;
        }
        else if (align & TEXTALIGN_RIGHT)
        {
            x = rcTextBox.fRight;
        }

        if (align & TEXTALIGN_VCENTER)
        {
            y = rcTextBox.fTop + (rcTextBox.height() - rcText.height()) / 2;
        }
        else if (align & TEXTALIGN_BOTTOM)
        {
            y = rcTextBox.fBottom - rcText.height();
        }
        SkPaint::FontMetrics fm;
        m_pPaint->getFontMetrics(&fm);
        m_pCanvas->drawText(text.c_str(), text.length(), x, y - fm.fAscent, *m_pPaint);
    }
}

void Graphics::DrawBitmap(Bitmap* pBitmap, Type::MiniRect* rcSrc, const Type::MiniRect& rcDst)
{
    if (!m_pCanvas) return;
    SkRect rectDst;
    rectDst.set(SkIntToScalar(rcDst.left), SkIntToScalar(rcDst.top), SkIntToScalar(rcDst.right), SkIntToScalar(rcDst.bottom));

    if (rcSrc)
    {
        SkIRect rectSrc;
        rectSrc.set(rcSrc->left, rcSrc->top, rcSrc->right, rcSrc->bottom);
        m_pCanvas->drawBitmapRect(*((SkBitmap*)pBitmap->GetBitmapDevice()), &rectSrc, rectDst, m_pPaint);
    }
    else
    {
        m_pCanvas->drawBitmapRect(*((SkBitmap*)pBitmap->GetBitmapDevice()), nullptr, rectDst, m_pPaint);
    }
}

void Graphics::DrawRectangle(const MiniRect& rect)
{
    if (!m_pCanvas) return;
    m_pCanvas->drawRectCoords(SkIntToScalar(rect.left), SkIntToScalar(rect.top), 
        SkIntToScalar(rect.right), SkIntToScalar(rect.bottom), *m_pPaint);
}

void Graphics::DrawCircle(const MiniPoint& pt, uint radius)
{
    if (!m_pCanvas) return;
    m_pCanvas->drawCircle(SkIntToScalar(pt.x), SkIntToScalar(pt.x), 
        SkIntToScalar(radius), *m_pPaint);
}

void Graphics::DrawLine(const MiniPoint& ptSrc, const MiniPoint& ptDst)
{
	if (!m_pCanvas) return;
	m_pCanvas->drawLine(SkIntToScalar(ptSrc.x), SkIntToScalar(ptSrc.y),
		SkIntToScalar(ptDst.x), SkIntToScalar(ptDst.y), *m_pPaint);
}